from __future__ import with_statement

from django.conf import settings
from django.db import models, transaction
from django.shortcuts import render_to_response, Http404, get_object_or_404, HttpResponseRedirect
from django.template import RequestContext
from django.utils.html import escape
from django.utils.datastructures import MultiValueDict
from django.views.decorators.http import require_POST
from django.db.models.signals import pre_delete 
from django.dispatch import receiver
from django.views.decorators.csrf import csrf_protect
from django.contrib import messages
from django.contrib.contenttypes.models import ContentType

from apps import files
from apps.files import signals,FilePostBadRequest
from apps.files.models import get_file_upload_path
from apps.course.functions import course_reverse
from apps.course.models import Course
from apps.participants.models import CourseParticipant

from filetransfers.api import serve_file as serve

import logging
import os

logger = logging.getLogger('pet.apps.files.views.files')

def serve_file(request, content_type, object_pk, filename, course=None):
	#TheCourse = Course.find_or_404(course,"\t Course %s not found" % course) if course else None
	#TheUser = request.user
	#TheCourseParticipant = CourseParticipant.find(course,user=TheUser) if course else None
	
	content_type = get_object_or_404(ContentType,name=content_type)
	TheFile = files.get_model().objects.filter(content_type=content_type,object_pk=object_pk,filename=filename)
	
	if not TheFile:
		logger.http404("File %s not found for object %s.%s" % (filename,content_type,object_pk))
		raise Http404
	
	#Get last version of file that matches filename
	TheFile = TheFile[len(TheFile)-1]
	
	if getattr(TheFile.content_object,"file_download_check",None) and not TheFile.content_object.file_download_check(request,TheFile,logger):
		logger.http404("Object %s rejected file download request for user %s" % (TheFile.content_object,request.user))
		raise Http404
	
	return serve(request,TheFile.file,save_as=False)

@receiver(pre_delete,sender=files.get_model())
def delete_file(sender, instance, **kwargs):
	path = os.path.join(settings.MEDIA_ROOT,get_file_upload_path(instance,instance.filename))
	if os.path.isfile(path):
		logger.info("Deleting file %s from disk" % path)
		os.remove(path)

@require_POST
def check_permissions(request, using=None, course=None):
	"""
	Check file permissions.

	Checks POST data for correct file attributes and permissions
	"""
	logger.fxn_entry("using=%s, course=%s\n" % (using,course))

	TheCourse = Course.RequestCourse(request)

	# Fill out some initial data fields from an authenticated user, if present
	data = request.POST.copy()

	# Look up the object we're trying to file about
	model, target, error = files.get_content_object(data,using)
	if error: logger.error("Error posting file: %s",error); return False, error
	
	# Construct the file form
	for name in request.FILES:
		file_objects = request.FILES.getlist(name)
		if name.endswith('[]'): name = name[:-2]
		for file in file_objects:
			full_data = data
			full_data['id_'+name] = file.name
			new_files = MultiValueDict()
			new_files.setlist(name,[file])
			form = files.get_form()(target, data=data, files=new_files)

			# Check security information
			if form.security_errors():
				logger.error("Error posting file: Bad Request: Failed security verificiation: %s ",escape(str(form.security_errors())))
				return False, FilePostBadRequest(
					"The file form failed security verification: %s" % \
						escape(str(form.security_errors())))
		
			# Check file save permissions
			 #ALSO: Check that courses match!
			if form.target_object:
				#Check for public upload permission
				if not getattr(form.target_object,'allow_public_file_upload',False):
					#Not public, check for course authentication
					TheCourseParticipant = CourseParticipant.find(course=TheCourse,user=request.user)
					if not TheCourseParticipant or TheCourseParticipant.status != "INSTRUCTOR" and TheCourseParticipant.status != "ASSISTANT":
						if not TheCourseParticipant or not getattr(form.target_object,'allow_student_file_upload',False):
							logger.error("No permission for user %s to upload file to object %s (a %s)" % (request.user, form.target_object, form.target_object.__class__))
							False, Http404
						# else: User not part of this course or object doesn't allow student modifications
					# else: User is an instructor or assistant, go for it!
					
					#Check that courses match if it contains a course
					if hasattr(form.target_object,'course'):
						courses = getattr(form.target_object,'course_set',None) or getattr(form.target_object,'courses',None) or getattr(form.target_object,'course')
						if isinstance(courses,str):
							if not TheCourse.course_id in courses:
								logger.http404("User %s trying to upload file for object %s from course %s" % (request.user, form.target_object, TheCourse))
								False, Http404
						else:
							if not courses.filter(course_id=TheCourse.course_id):
								logger.http404("User %s trying to upload file for object %s from course %s" % (request.user, form.target_object, TheCourse))
								False, Http404
				#else: It is public, go for it!
			
	logger.fxn_exit("File passes checks")
	return True, "Success"

@csrf_protect
@require_POST
def post_file(request, next=None, using=None, course=None):
	"""
	Post a file.

	HTTP POST is required. If ``POST['submit'] == "preview"`` or if there are
	errors a preview template, ``files/preview.html``, will be rendered.
	"""
	logger.fxn_entry("next=%s, using=%s, course=%s\n" % (next,using,course))

	Pass, Reason = check_permissions(request,using,course)

	if not Pass:
		if Reason == Http404: raise Http404
		else: return Reason
		
	# Fill out some initial data fields from an authenticated user, if present
	data = request.POST.copy()
	
	# Check to see if the POST data overrides the view's next argument.
	next = data.get("next", next)
	if not next:
		next = course_reverse('course_index',Course.RequestCourse(request))

	# Look up the object we're trying to file about
	model, target, error = files.get_content_object(data,using)
	
	# Do we want to preview the file?
	preview = "preview" in data

	# Construct the file form
	for name in request.FILES:
		file_objects = request.FILES.getlist(name)
		if name.endswith('[]'): name = name[:-2]
		for file in file_objects:
			full_data = data
			full_data['id_'+name] = file.name
			new_files = MultiValueDict()
			new_files.setlist(name,[file])
			form = files.get_form()(target, data=data, files=new_files)

			# If there are errors or if we requested a preview show the file
			if form.errors or preview:
				template_list = [
					# These first two exist for purely historical reasons.
					# Django v1.0 and v1.1 allowed the underscore format for
					# preview templates, so we have to preserve that format.
					"files/%s_%s_preview.html" % (model._meta.app_label, model._meta.module_name),
					"files/%s_preview.html" % model._meta.app_label,
					# Now the usual directory based template heirarchy.
					"files/%s/%s/preview.html" % (model._meta.app_label, model._meta.module_name),
					"files/%s/preview.html" % model._meta.app_label,
					"files/preview.html",
				]
				return render_to_response(
					template_list, {
						"file" : form.data.get("file", ""),
						"form" : form,
						"next": next,
					},
					RequestContext(request, {})
				)
			
			# Otherwise create the file
			file = form.get_file_object()
			file.ip_address = request.META.get("REMOTE_ADDR", None)
			if request.user.is_authenticated():
				file.user = request.user

			# Signal that the file is about to be saved
			responses = signals.file_will_be_posted.send(
				sender  = file.__class__,
				file = file,
				request = request
			)
		
			for (receiver, response) in responses:
				if response == False:
					logger.error("file_will_be_posted receiver %s killed the file" % receiver.__name__)
					return FilePostBadRequest(
						"file_will_be_posted receiver %r killed the file" % receiver.__name__)
		
			# Save the file and signal that it was saved
			with transaction.commit_on_success(): logger.info("File saved"); file.save()
			signals.file_was_posted.send(
				sender  = file.__class__,
				file = file,
				request = request
			)
	logger.fxn_exit("Returning a redirect to %s" % next)
	return HttpResponseRedirect(next)

